﻿

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data.SqlClient;
using System.Security.Permissions;
using System.Web;
using EasyBuildingCMS.BLL.CoreEngine;
using System.Collections.Specialized;
using System.Configuration.Provider;
using System.Web.Caching;

namespace EasyBuildingCMS.BLL.SiteMap
{
    /*
     * 
     * This class is modified based on the TBHSiteMapProvider from TheBeerHouse. The difference with TheBeerHouse is that 
     * the root node of EasyBuildingCMS is not stored in Datebase, but rather dynamically generated.Also, the sitemap is directly
     * generated from page table. there is no extra sitemap table.
     * 
     */

    [SqlClientPermission(SecurityAction.Demand, Unrestricted = true)]
    public class EBCSQLSiteMapProvider : StaticSiteMapProvider
    {
        private const string _cacheDependencyName = "__SiteMapCacheDependency";
        private const string _errmsg4 = "Invalid parent ID";
        private readonly object _lock = new object();
        private readonly Dictionary<int, SiteMapNode> _nodes = new Dictionary<int, SiteMapNode>();

        private SiteMapNode _root;
        protected List<Page> lSiteMapNodes;

        public override void Initialize(string name, NameValueCollection config)
        {
            // Verify that config isn't null 
            if (config == null)
            {
                throw new ArgumentNullException("config");
            }

            // Assign the provider a default name if it doesn't have one 
            if (String.IsNullOrEmpty(name))
            {
                name = "EBCSQLSiteMapProvider";
            }

            // Add a default "description" attribute to config if the 
            // attribute doesnt exist or is empty 
            if (string.IsNullOrEmpty(config["description"]))
            {
                config.Remove("description");
                config.Add("description", "SQL site map provider");
            }

            // Call the base class's Initialize method 
            base.Initialize(name, config);

            if (config["securityTrimmingEnabled"] != null)
            {
                config.Remove("securityTrimmingEnabled");
            }

            // Throw an exception if unrecognized attributes remain 
            if (config.Count > 0)
            {
                string attr = config.GetKey(0);
                if (!String.IsNullOrEmpty(attr))
                {
                    throw new ProviderException("Unrecognized attribute: " + attr);
                }
            }
        }

        public override SiteMapNode BuildSiteMap()
        {
            lock (_lock)
            {
                // Return immediately if this method has been called before 
                if (_root != null)
                {
                    return _root;
                }

                using (var pageRepositry = new PageRepository())
                {
                    lSiteMapNodes = pageRepositry.GetSiteMap();

                    _root = this.CreateRootNode();
                    AddNode(_root, null);

                    if (lSiteMapNodes.Count > 0)
                    {
                        AddChildNodes(_root, null);
                    }
                }

                return _root;
            }
        }

        private void AddChildNodes(SiteMapNode vParentNode, int? SiteMapId)
        {
            List<Page> lChildNodes = (from lChildren in lSiteMapNodes
                                      where lChildren.ParentPageID == SiteMapId
                                      select lChildren).ToList();

            foreach (Page lChildNode in lChildNodes)
            {
                SiteMapNode lnode = CreateSiteMapNodeFromSiteMapEntity(lChildNode);
                AddNode(lnode, vParentNode);
                AddChildNodes(lnode, lChildNode.PageID);
            }
        }

        protected override SiteMapNode GetRootNodeCore()
        {
            lock (_lock)
            {
                BuildSiteMap();
                return _root;
            }
        }

        // Helper methods 

        private SiteMapNode CreateRootNode()
        {
            var _node = new SiteMapNode(this, "root111", "rootNode", "Root", "Root", null, null, null, null);

            _nodes.Add(0, _node);
            return _node;
        }

        private SiteMapNode CreateSiteMapNodeFromSiteMapEntity(Page node)
        {

            // Create a SiteMapNode 
            var _node = new SiteMapNode(this, node.PageID.ToString(), node.VirtualHandleName, node.PageNavigationText,
                node.PageDescription, null, null, null, null);

            // Record the node in the _nodes dictionary 
            _nodes.Add(node.PageID, _node);

            // Return the node 

            return _node;
        }

        private SiteMapNode GetParentNodeFromSiteMapEntity(Page node)
        {
            // Get the parent ID from the DataReader 
            int? pid = node.ParentPageID;
            if (pid == null)
                pid = 0;

            // Make sure the parent ID is valid 
            if (!_nodes.ContainsKey((int)pid))
            {
                throw new ProviderException(_errmsg4);
            }

            // Return the parent SiteMapNode 
            return _nodes[(int)pid];
        }

        private void OnSiteMapChanged(string key, object item, CacheItemRemovedReason reason)
        {
            lock (_lock)
            {
                if (key == _cacheDependencyName && reason == CacheItemRemovedReason.DependencyChanged)
                {
                    // Refresh the site map 
                    Clear();
                    _nodes.Clear();
                    _root = null;
                }
            }
        }
    }
}